package me.gacl.utils;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

import javax.sql.DataSource;

import javax.naming.Context;
import javax.naming.InitialContext;

import org.apache.commons.dbcp2.BasicDataSourceFactory;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JdbcUtils {

	private static String driver = null;
	private static String url = null;
	private static String username = null;
	private static String password = null;

	static {
		try {
			// 读取db.properties文件中的数据库连接信息
			InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db1.properties");
			Properties prop = new Properties();
			prop.load(in);

			// 获取数据库连接驱动
			driver = prop.getProperty("driver");
			// 获取数据库连接URL地址
			url = prop.getProperty("url");
			// 获取数据库连接用户名
			username = prop.getProperty("username");
			// 获取数据库连接密码
			password = prop.getProperty("password");
			// 加载数据库驱动
			Class.forName(driver);

		} catch (Exception e) {
			// TODO: handle exception
			throw new ExceptionInInitializerError(e);
		}
	}

	/**
	 * @Method: getConnection
	 * @Description: 获取数据库连接对象
	 * @Anthor:孤傲苍狼
	 *
	 * @return Connection数据库连接对象
	 * @throws SQLException
	 */
	public static Connection getConnection() throws SQLException {
		return DriverManager.getConnection(url, username, password);
	}

	/**
	 * @Method: release
	 * @Description: 释放资源，
	 *               要释放的资源包括Connection数据库连接对象，负责执行SQL命令的Statement对象，存储查询结果的ResultSet对象
	 * @Anthor:孤傲苍狼
	 *
	 * @param conn
	 * @param st
	 * @param rs
	 */
	public static void release(Connection conn, Statement st, ResultSet rs) {
		if (rs != null) {
			try {
				// 关闭存储查询结果的ResultSet对象
				rs.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
			rs = null;
		}
		if (st != null) {
			try {
				// 关闭负责执行SQL命令的Statement对象
				st.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		if (conn != null) {
			try {
				// 关闭Connection数据库连接对象
				conn.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	/*
	 * 二、使用元数据封装简单的JDBC框架 系统中所有实体对象都涉及到基本的CRUD操作
	 * 所有实体的CUD操作代码基本相同，仅仅发送给数据库的SQL语句不同而已，因此可以把CUD操作的所有相同代码抽取到工具类的一个update方法中，
	 * 并定义参数接收变化的SQL语句。
	 * 实体的R操作，除SQL语句不同之外，根据操作的实体不同，对ResultSet的映射也各不相同，因此可义一个query方法，
	 * 除以参数形式接收变化的SQL语句外，可以使用策略模式由qurey方法的调用者决定如何把ResultSet中的数据映射到实体对象中。
	 */
	//封装通用的update方法和qurey方法
    /**
    * @Method: update
    * @Description: 万能更新
    * 所有实体的CUD操作代码基本相同，仅仅发送给数据库的SQL语句不同而已，
    * 因此可以把CUD操作的所有相同代码抽取到工具类的一个update方法中，并定义参数接收变化的SQL语句
    * @Anthor:孤傲苍狼
    * @param sql 要执行的SQL
    * @param params 执行SQL时使用的参数
    * @throws SQLException
    */ 
	public static void update(String sql,Object[] params) throws SQLException {
		Connection conn = null;
		PreparedStatement st = null;
		ResultSet rs = null;
		try {
			conn = getConnection();
			st = conn.prepareStatement(sql);
			for(int i = 0; i < params.length ; i++) {
				st.setObject(i+1, params[i]);
			}
			int num = st.executeUpdate();
			if(num > 0) {
				System.out.println("update 操作执行成功！！");
			}
		} catch (Exception e) {
			// TODO: handle exception
			throw e;
		}finally {
			release(conn, st, rs);
		}
	}
	
    /**
    * @Method: query
    * @Description:万能查询
    * 实体的R操作，除SQL语句不同之外，根据操作的实体不同，对ResultSet的映射也各不相同，
    * 因此可义一个query方法，除以参数形式接收变化的SQL语句外，可以使用策略模式由qurey方法的调用者决定如何把ResultSet中的数据映射到实体对象中。
    * @Anthor:孤傲苍狼
    *
    * @param sql 要执行的SQL
    * @param params 执行SQL时使用的参数
    * @param rsh 查询返回的结果集处理器
    * @return
     * @throws Exception 
    * @throws SQLException
    */ 
	public static Object query(String sql,Object[] params,ResultSetHandler rsh) throws Exception {
		Connection conn = null;
		PreparedStatement st = null;
		ResultSet rs = null;
		try {
			conn = getConnection();
			st = conn.prepareStatement(sql);
            for(int i=0;i<params.length;i++){
                st.setObject(i+1, params[i]);
            }
            rs = st.executeQuery();
            /**
             * 对于查询返回的结果集处理使用到了策略模式，
             * 在设计query方法时，query方法事先是无法知道用户对返回的查询结果集如何进行处理的，即不知道结果集的处理策略，
             * 那么这个结果集的处理策略就让用户自己提供，query方法内部就调用用户提交的结果集处理策略进行处理
             * 为了能够让用户提供结果集的处理策略，需要对用户暴露出一个结果集处理接口ResultSetHandler
             * 用户只要实现了ResultSetHandler接口，那么query方法内部就知道用户要如何处理结果集了
             */
            return rsh.handler(rs);
		} catch (Exception e) {
			// TODO: handle exception
			throw e;
		}finally {
			release(conn, st, rs);
		}
				
	}
	
	public static DataSource getDataSource(int type) {
		if(type == 0) {//自定义jdbcPool
			JdbcPool pool = new JdbcPool();
			return pool;
		}
		if(type == 1) {//C3P0
			ComboPooledDataSource ds = null;
		        try{
		            //通过代码创建C3P0数据库连接池
		            /*ds = new ComboPooledDataSource();
		            ds.setDriverClass("com.mysql.jdbc.Driver");
		            ds.setJdbcUrl("jdbc:mysql://localhost:3306/jdbcstudy");
		            ds.setUser("root");
		            ds.setPassword("XDP");
		            ds.setInitialPoolSize(10);
		            ds.setMinPoolSize(5);
		            ds.setMaxPoolSize(20);*/
		            
		            //通过读取C3P0的xml配置文件创建数据源，C3P0的xml配置文件c3p0-config.xml必须放在src目录下
		            //ds = new ComboPooledDataSource();//使用C3P0的默认配置来创建数据源
		            ds = new ComboPooledDataSource("MySQL");//使用C3P0的命名配置来创建数据源
		            
		        }catch (Exception e) {
		            throw new ExceptionInInitializerError(e);
		        }
		        return ds;
		}
		if(type == 2) {//DBCP
			DataSource ds = null;
				try {
					//加载dbcpconfig.properties配置文件
					InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
					Properties prop = new Properties();
					prop.load(in);
					//创建数据源
					ds = BasicDataSourceFactory.createDataSource(prop);
					System.out.println("ds:"+ds);
					System.out.println("********");
					
//					String driverClassName = prop.getProperty("driverClassName");
//					String url = prop.getProperty("url");
//					String username = prop.getProperty("username");
//					String password = prop.getProperty("password");
//					
//					BasicDataSource ds1 = new BasicDataSource();
//					ds1.setDriverClassName(driverClassName);
//					ds1.setUrl(url);
//					ds1.setUsername(username);
//					ds1.setPassword(password);
//					ds = ds1;
//					System.out.println("***done*****");
				} catch (Exception e) {
					// TODO: handle exception
					e.printStackTrace();
					//throw new ExceptionInInitializerError();
				}
				return ds;
			}
		if(type == 3) {//JNDI
		    DataSource ds = null;
		    //在静态代码块中创建数据库连接池
		        try{
		             //初始化JNDI
		            Context initCtx = new InitialContext();
		             //得到JNDI容器
		            Context envCtx = (Context) initCtx.lookup("java:comp/env");
		             //从JNDI容器中检索name为jdbc/datasource的数据源
		            ds = (DataSource)envCtx.lookup("jdbc/datasource");
		        }catch (Exception e) {
		            throw new ExceptionInInitializerError(e);
		        }
		     return ds;
		}
		return null;
	}
}

